import window from '@ohos.window';
import display from '@ohos.display';
import Constant, { DisplayPage, WindowPage } from './Constant'
import Log from '../Utils/Log'

const TAG: string = 'mw_DisplayHelper: '

export default class DisplayHelper {
  public defaultDisplay: display.Display = undefined
  private displayDevices: Array<DisplayPage> = []

  private windowPages: Array<WindowPage> = [
    new WindowPage(Constant.DEFAULT_UNSELECT_WINDOW_PAGE),
    new WindowPage('pages/WindowPageA'),
    new WindowPage('pages/WindowPageB'),
    new WindowPage('pages/WindowPageC')]

  private constructor() {
    Log.warn(TAG + 'constructor called.')
    AppStorage.SetOrCreate<Array<WindowPage>>('windowPages', this.windowPages)
    this.loadDefaultDisplay()
    this.loadAllDisplays()

    this.initDisplayChangeListener()
  }

  public static getInstance(): DisplayHelper {
    return globalThis.DisplayManager ??= new DisplayHelper()
  }

  /**
   * 获取默认display对象(主屏)
   */
  private loadDefaultDisplay() {
    try {
      this.defaultDisplay = display.getDefaultDisplaySync()
      Log.info(TAG + 'loadDefaultDisplay. defaultDisplay id: ' + this.defaultDisplay?.id)
    } catch (err) {
      Log.error(TAG + 'loadDefaultDisplay occur error. errInfo: ' + JSON.stringify(err))
    }
  }

  private async loadAllDisplays() {
    let displays: Array<display.Display> = await display.getAllDisplays()
    for (let display of displays) {
      if (this.displayDevices.findIndex(item => item.display?.id === display.id) < 0) {
        Log.info(TAG + 'new display added. detail: ' + JSON.stringify(display))
        this.displayDevices.push(new DisplayPage(display))
        this.createMainWindow(display)
      }
    }
    AppStorage.SetOrCreate<Array<DisplayPage>>('displayDevices', this.displayDevices)
  }

  /**
   * 显示屏添加/删除监听
   */
  private initDisplayChangeListener() {
    display.on('add', displayId => {
      Log.info(TAG + 'add new display. id: ' + JSON.stringify(displayId))
      this.loadAllDisplays()
    })

    display.on('remove', displayId => {
      Log.info(TAG + 'remove display. id: ' + JSON.stringify(displayId))
      let delIndex: number = this.displayDevices.findIndex(item => item.display.id === displayId)
      if (delIndex > 0) {
        let pagePaths: Array<string> = this.displayDevices[delIndex].pagePaths
        for (let pagePath of pagePaths) {
          this.destroySubWindow(displayId, pagePath)
        }
        this.destroyMainWindow(displayId)
        this.displayDevices.splice(delIndex, 1)

        AppStorage.SetOrCreate<Array<DisplayPage>>('displayDevices', this.displayDevices)
      }
    })
  }

  /**
   * 在指定屏幕上创建主window(新屏幕插入时，默认桌面窗口，不支持隐藏;屏幕拔出时，隐藏销毁本窗口)
   * @param display
   */
  private createMainWindow(display: display.Display) {
    if (!Constant.CONFIG.IS_CREATE_MAIN_WINDOW || display.id === this.defaultDisplay?.id) {
      //主屏不需要创建主窗口
      return
    }
    window.createWindow({
      ctx: globalThis.globalContext,
      name: Constant.MAIN_WINDOW_PREFIX + display.id,
      windowType: window.WindowType.TYPE_DESKTOP,
      displayId: display.id
    }).then((resultWindow: window.Window) => {
      resultWindow.resize(display.width, display.height)
      resultWindow.setWindowMode(window.WindowMode.FULLSCREEN)
      resultWindow.setUIContent(Constant.DEFAULT_MAIN_WINDOW_PAGE)
      Log.info(`${TAG}create main window ${display.id} success.`)

      resultWindow.showWithAnimation()
    }).catch(err => {
      Log.error(TAG + 'create main window failed. reason: ' + JSON.stringify(err))
    })
  }

  /**
   * 在指定屏幕上创建子window
   * @param display window显示屏幕
   * @param pagePath window显示内容界面
   */
  public createSubWindow(display: display.Display, pagePath: string) {
    window.createWindow({
      ctx: globalThis.globalContext,
      name: Constant.SUB_WINDOW_PREFIX + display.id + '_' + pagePath,
      windowType: window.WindowType.TYPE_FLOAT,
      displayId: display.id
    }).then((resultWindow: window.Window) => {
      resultWindow.resize(Constant.SUB_WINDOW_WIDTH, Constant.SUB_WINDOW_HEIGHT)
      resultWindow.setWindowMode(window.WindowMode.FLOATING)
      resultWindow.moveWindowTo(0, 56)
      resultWindow.setUIContent(pagePath)
      Log.info(`${TAG}create sub window ${display.id} success.`)

      resultWindow.showWithAnimation()
      this.updateWindowPages(pagePath, display.id)
    }).catch(reason => {
      Log.error(TAG + 'create sub window failed. reason: ' + JSON.stringify(reason))
    })
  }

  /**
   * 查找window
   * @param displayId
   * @param pagePath
   * @returns
   */
  public findWindow(displayId: number, pagePath: string): window.Window {
    let resultWindow = undefined
    try {
      resultWindow = window.findWindow(Constant.SUB_WINDOW_PREFIX + displayId + '_' + pagePath)
    } catch (err) {
      Log.warn(TAG + 'no window displayId: ' + displayId + ' pagePath: ' + pagePath + ' found.')
    }
    return resultWindow
  }

  /**
   * 窗口显示或消失
   * @param displayId
   * @param pagePath
   * @param forceHide
   */
  public showOrHideWindow(displayId: number, pagePath: string, forceHide: boolean = false) {
    Log.info(TAG + 'showOrHideWindow. displayId: ' + displayId + ' pagePath: ' + pagePath)
    let resultWindow: window.Window = this.findWindow(displayId, pagePath)
    if (!resultWindow.isWindowShowing() && !forceHide) {
      resultWindow.showWithAnimation()
      this.updateWindowPages(pagePath, displayId)
    } else {
      resultWindow.hideWithAnimation()
      this.updateWindowPages(pagePath)
    }
  }

  /**
   * 销毁指定屏幕上window
   * @param displayId
   * @param pagePath
   */
  public destroySubWindow(displayId: number, pagePath: string) {
    let resultWindow: window.Window = this.findWindow(displayId, pagePath)
    if (resultWindow.isWindowShowing()) {
      resultWindow.hideWithAnimation()
      this.updateWindowPages(pagePath)
    }
    resultWindow.destroyWindow()
    Log.info(`${TAG}destroy sub window. displayId: ${displayId} pagePath: ${pagePath} success.`)
  }

  private destroyMainWindow(displayId: number) {
    if (!Constant.CONFIG.IS_CREATE_MAIN_WINDOW || displayId === this.defaultDisplay?.id) {
      return
    }
    try {
      let resultWindow = window.findWindow(Constant.MAIN_WINDOW_PREFIX + displayId)
      if (resultWindow?.isWindowShowing()) {
        resultWindow.hideWithAnimation()
      }
      resultWindow?.destroyWindow()
      Log.info(`${TAG}destroy main window ${displayId} success.`)
    } catch (err) {
      Log.error(TAG + 'find MainWindow occur err. errInfo: ' + JSON.stringify(err))
    }
  }

  /**
   * 销毁所有屏幕上创建的window
   */
  public destroyAllWindow() {
    for (let display of this.displayDevices) {
      for (let pagePath of display.pagePaths) {
        this.destroySubWindow(display.display?.id, pagePath)
      }
      this.destroyMainWindow(display.display?.id)
    }
    display.off('add')
    display.off('remove')
    AppStorage.SetOrCreate<Array<DisplayPage>>('displayDevices', [])
  }

  /**
   * 更新 pagePath 界面是否正在某个屏幕窗口显示(单个界面同时只能在一个屏幕窗口显示)
   * @param pagePath
   * @param showingDisplayId
   */
  private updateWindowPages(pagePath: string, showingDisplayId: number = -1) {
    let windowPage = this.windowPages.find(value => value.pagePath === pagePath)
    if (windowPage) {
      windowPage.showingDisplayId = showingDisplayId
      AppStorage.SetOrCreate<Array<WindowPage>>('windowPages', this.windowPages)
      Log.info(TAG + 'updateWindowPages. windowPages: ' + JSON.stringify(this.windowPages))
    }
  }

  /**
   * 更新display中子窗口路径
   * @param displayId
   * @param pagePath
   */
  public updateDisplayDevices(displayId: number, pagePath: string) {
    let index: number = this.displayDevices.findIndex(item => item.display?.id === displayId)
    if (index >= 0) {
      let pagePaths: Array<string> = this.displayDevices[index].pagePaths
      let delIndex: number = pagePaths.findIndex(value => value === pagePath)
      if (delIndex >= 0) {
        pagePaths.splice(delIndex, 1)
      } else {
        pagePaths.push(pagePath)
      }
      this.displayDevices[index].pagePaths = pagePaths
      AppStorage.SetOrCreate<Array<DisplayPage>>('displayDevices', this.displayDevices)
      Log.info(TAG + 'updateDisplayDevices. displayDevices: ' + JSON.stringify(this.displayDevices))
    }
  }
}